/*
 * Copyright (C) 2012-2013 Michael L.R. Marques
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * Contact: michaellrmarques@gmail.com
 */
package com.jm.jmsql.objects.base;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Label;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;

/**
 * 
 * @author Michael L.R. Marques
 */
public abstract class JMSqlObject implements List<JMSqlObject>, Comparable<JMSqlObject>, ListCellRenderer {
    
    // Puts the object into an editable state
    private boolean editingMode;
    // If the children have already been loaded
    private boolean loaded;
    // 
    private String name;
    //
    private JMSqlObject parent;
    //
    private List<JMSqlObject> children;
    
    /**
     * 
     */
    public JMSqlObject() {
        this(null, null, null);
    }
    
    /**
     * 
     * @param name 
     */
    public JMSqlObject(String name) {
        this(name, new ArrayList());
    }
    
    /**
     * 
     * @param name
     * @param children 
     */
    public JMSqlObject(String name, List<JMSqlObject> children) {
        this(null, name, children);
    }
    
    /**
     * 
     * @param parent
     * @param name 
     */
    public JMSqlObject(JMSqlObject parent, String name) {
        this(parent, name, new ArrayList());
    }
    
    /**
     * 
     * @param parent
     * @param name
     * @param children 
     */
    public JMSqlObject(JMSqlObject parent, String name, List<JMSqlObject> children) {
        super();
        this.editingMode = false;
        this.parent = parent;
        this.name = name;
        this.children = children;
    }
    
    /**
     * 
     * @return 
     */
    public final boolean isLoaded() {
        return this.loaded;
    }
    
    /**
     * 
     */
    public void load() {
        this.loaded = true;
    }
    
    /**
     * 
     */
    public void reload() {
        clear();
        load();
    }
    
    /**
     * 
     * @return 
     */
    public boolean isInEditMode() {
        return this.editingMode;
    }
    
    /**
     * 
     * @param editingMode 
     */
    public void setEditMode(boolean editingMode) {
        this.editingMode = editingMode;
    }

    /**
     *
     * @return String
     */
    public final String getName() {
        return this.name;
    }
    
    /**
     * 
     * @param name 
     */
    public final void setName(String name) {
        this.name = name;
    }
    
    /**
     * 
     * @return 
     */
    public Icon getIcon() {
        return new ImageIcon(getClass().getClassLoader().getResource("com/jm/jmsql/icons/database.png"));
    }

    /**
     *
     * @return
     */
    public final boolean hasParent() {
        return this.parent != null;
    }

    /**
     *
     * @return JMSqlObject
     */
    public final JMSqlObject getParent() {
        return this.parent;
    }
    
    /**
     * 
     * @param parent
     */
    public final void setParent(JMSqlObject parent) {
        this.parent = parent;
    }
    
    /**
     * 
     * @return 
     */
    public boolean isSortable() {
        return !isEmpty();
    }

    /**
     *
     * @return
     */
    public Object[] getPath() {
        List<Object> path = new ArrayList();
        JMSqlObject object = this;
        path.add(object);
        while (object != null &&
                    object.hasParent()) {
            path.add(0, object.getParent());
            object = object.getParent();
        }
        return path.toArray();
    }

    /**
     *
     * @return
     */
    public int[] getIndices() {
        int[] indices = new int[size()];
        for (int i = 0; i < size(); i++) {
            indices[i] = i;
        }
        return indices;
    }
    
    /**
     * 
     * @return 
     */
    public String getProperties() {
        StringBuilder properties = new StringBuilder();
        properties.append("<html><body>");
        for (int i = 0; i < getColumnCount(); i++) {
            properties.append(getColumnName(i));
            properties.append(": ");
            if (getColumn(i).getClass().equals(Boolean.class)) {
                properties.append(Boolean.parseBoolean(getColumn(i).toString()) ? "Yes" : "No");
            } else {
                properties.append(getColumn(i));
            }
            if (i < getColumnCount()-1) {
                properties.append("<br>");
            }
        }
        properties.append("</body></html>");
        return properties.toString();
    }
    
    /**
     * 
     * @return 
     */
    public abstract JMSqlObject.Type getObjectType();
    
    /**
     * 
     * @return 
     */
    public abstract int getColumnCount();
    
    /**
     * Get the name of the value by index
     * @param index
     * @return 
     */
    public abstract String getColumnName(int index);
    
    /**
     * Get the value by the index
     * @param index
     * @return 
     */
    public abstract Object getColumn(int index);
    
    /**
     * Is the value by the index editable
     * @param index
     * @return 
     */
    public abstract boolean isColumnEditable(int index);
    
    /**
     * Set the value by the index
     * @param index 
     * @param object 
     */
    public abstract void setColumn(int index, Object object);
    
    /**
     * 
     * @return 
     */
    @Override
    public int hashCode() {
        return super.hashCode();
    }
    
    /**
     * 
     * @param that
     * @return 
     */
    @Override
    public boolean equals(Object that) {
        return that instanceof JMSqlObject &&
                        ((JMSqlObject) that).getName().equals(getName());
    }
    
    /**
     * 
     * @return 
     */
    @Override public String toString() {
        return "Name: " + this.name + " Children: " + size() + (hasParent() ? " Parent: " + getParent().getName(): "");
    }
    
    /**
     * 
     * @param o
     * @return 
     */
    @Override
    public int compareTo(JMSqlObject o) {
        return getName().compareTo(o.getName());
    }
    
    /**
     * 
     * @param list
     * @param value
     * @param index
     * @param selected
     * @param cellHasFocus
     * @return 
     */
    @Override 
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean cellHasFocus) {
        JLabel label = new JLabel(getName(), getIcon(), Label.LEFT);
        label.setFont(new Font("Arial", Font.PLAIN, 11));
        label.setForeground(selected ? Color.gray : Color.black);
        label.setBackground(Color.white);
        label.setToolTipText(getObjectType().toString());
        return label;
    }
    
    /**
     * 
     * @param index
     * @return 
     */
    @Override
    public JMSqlObject get(int index) {
        return this.children.get(index);
    }
    
    /**
     * 
     * @return 
     */
    @Override
    public int size() {
        return this.children.size();
    }
    
    /**
     * 
     * @return 
     */
    @Override
    public boolean isEmpty() {
        return this.children.isEmpty();
    }
    
    /**
     * 
     * @param o
     * @return 
     */
    @Override
    public boolean contains(Object o) {
        return this.children.contains(o);
    }
    
    /**
     * 
     * @return 
     */
    @Override
    public Iterator<JMSqlObject> iterator() {
        return this.children.iterator();
    }
    
    /**
     * 
     * @return 
     */
    @Override
    public Object[] toArray() {
        return this.children.toArray();
    }
    
    /**
     * 
     * @param <T>
     * @param a
     * @return 
     */
    @Override
    public <T> T[] toArray(T[] a) {
        return this.children.toArray(a);
    }
    
    /**
     * 
     * @param object
     * @return 
     */
    public boolean remove(JMSqlObject object) {
        if (isEmpty()) {
            return false;
        }
        return this.children.remove(object);
    }
    
    /**
     * 
     * @param object
     * @return 
     */
    @Override
    public boolean remove(Object object) {
        if (isEmpty()) {
            return false;
        }
        return this.children.remove(object);
    }
    
    /**
     * 
     * @param c
     * @return 
     */
    @Override
    public boolean containsAll(Collection<?> c) {
        return this.children.containsAll(c);
    }
    
    /**
     * 
     * @param c
     * @return 
     */
    @Override
    public boolean addAll(Collection<? extends JMSqlObject> c) {
        return this.children.addAll(c);
    }
    
    /**
     * 
     * @param index
     * @param c
     * @return 
     */
    @Override
    public boolean addAll(int index, Collection<? extends JMSqlObject> c) {
        return this.children.addAll(index, c);
    }
    
    /**
     * 
     * @param c
     * @return 
     */
    @Override
    public boolean removeAll(Collection<?> c) {
        return this.children.removeAll(c);
    }
    
    /**
     * 
     * @param c
     * @return 
     */
    @Override
    public boolean retainAll(Collection<?> c) {
        return this.children.removeAll(c);
    }
    
    /**
     * 
     */
    @Override
    public void clear() {
        this.loaded = false;
        this.children.clear();
    }
    
    /**
     * 
     * @param oldObject
     * @param newObject
     * @return 
     */
    public boolean set(JMSqlObject oldObject, JMSqlObject newObject) {
        //
        if (oldObject.equals(newObject)) {
            return false;
        }
        //
        if (!newObject.hasParent()) {
            newObject.setParent(this);
        }
        return set(indexOf(oldObject), newObject) != null;
    }
    
    /**
     * 
     * @param index
     * @param newObject
     * @return 
     */
    @Override
    public JMSqlObject set(int index, JMSqlObject newObject) {
        return this.children.set(index, newObject);
    }
    
    /**
     * 
     * @param object
     * @return 
     */
    @Override
    public boolean add(JMSqlObject object) {
        if (!object.hasParent()) {
            object.setParent(this);
        }
        return this.children.add(object);
    }
    
    /**
     * 
     * @param index
     * @param object 
     */
    @Override
    public void add(int index, JMSqlObject object) {
        if (!object.hasParent()) {
            object.setParent(this);
        }
        this.children.add(index, object);
    }
    
    /**
     * 
     * @param index
     * @return 
     */
    @Override
    public JMSqlObject remove(int index) {
        return this.children.remove(index);
    }
    
    /**
     * 
     * @param o
     * @return 
     */
    @Override
    public int indexOf(Object o) {
        return this.children.indexOf(o);
    }
    
    /**
     * 
     * @param o
     * @return 
     */
    @Override
    public int lastIndexOf(Object o) {
        return this.children.lastIndexOf(o);
    }
    
    /**
     * 
     * @return 
     */
    @Override
    public ListIterator<JMSqlObject> listIterator() {
        return this.children.listIterator();
    }
    
    /**
     * 
     * @param index
     * @return 
     */
    @Override
    public ListIterator<JMSqlObject> listIterator(int index) {
        return this.children.listIterator(index);
    }
    
    /**
     * 
     * @param type
     * @return 
     */
    public List<JMSqlObject> subList(Class<?> type) {
        List<JMSqlObject> list = new ArrayList();
        for (JMSqlObject child : this.children) {
            if (child.getClass().equals(type) ||
                    child.getClass().isInstance(type)) {
                list.add(child);
            }
        }
        return list;
    }
    
    /**
     * 
     * @param fromIndex
     * @param toIndex
     * @return 
     */
    @Override
    public List<JMSqlObject> subList(int fromIndex, int toIndex) {
        return this.children.subList(fromIndex, toIndex);
    }
    
    /**
     * Defines the event types
     * @author Michael L.R. Marques
     */
    public static final class Type {
        
        /**
         * 
         */
        private String typeString;
        
        /**
         * 
         * @param s 
         */
        private Type(String string) {
            this.typeString = string;
        }

        /**
         * Databases type object
         */
        public static final JMSqlObject.Type DATABASES = new JMSqlObject.Type("Databases");

        /**
         * Database type object
         */
        public static final JMSqlObject.Type DATABASE = new JMSqlObject.Type("Database");
        
        /**
         * Definition type object
         */
        public static final JMSqlObject.Type DEFINITION = new JMSqlObject.Type("Definition");
        
        /**
         * Catalog type object
         */
        public static final JMSqlObject.Type CATALOG = new JMSqlObject.Type("Catalog");
        
        /**
         * Schema type object
         */
        public static final JMSqlObject.Type SCHEMA = new JMSqlObject.Type("Schema");
        
        /**
         * Tables type object
         */
        public static final JMSqlObject.Type TABLES = new JMSqlObject.Type("Tables");
        
        /**
         * Procedures type object
         */
        public static final JMSqlObject.Type PROCEDURES = new JMSqlObject.Type("Procedures");
        
        /**
         * Views type object
         */
        public static final JMSqlObject.Type VIEWS = new JMSqlObject.Type("Views");
        
        /**
         * Table type object
         */
        public static final JMSqlObject.Type TABLE = new JMSqlObject.Type("Table");
        
        /**
         * Procedure type object
         */
        public static final JMSqlObject.Type PROCEDURE = new JMSqlObject.Type("Procedure");
        
        /**
         * View type object
         */
        public static final JMSqlObject.Type VIEW = new JMSqlObject.Type("View");
        
        /**
         * Column type object
         */
        public static final JMSqlObject.Type COLUMN = new JMSqlObject.Type("Column");
        
        /**
         * Procedure Column type object
         */
        public static final JMSqlObject.Type PROCEDURE_COLUMN = new JMSqlObject.Type("Procedure Column");

        /**
         * Converts the type to a string.
         * @return the string
         */
        @Override public String toString() {
            return this.typeString;
        }
        
    }
    
}
